//==============================================================================
//
// DDiscreteDistribution - the (discrete) distribution class in the ofc-library
//
//               Copyright (C) 2005  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2006-07-22 13:28:56 $ $Revision: 1.3 $
//
//==============================================================================

#include "ofc/DDiscreteDistribution.h"

#include <math.h>


#if _INTERFACE_

#include "ofc/config.h"

#include "ofc/DScore.h"
#include "ofc/DList.h"

//
// The DDiscreteDistribution class implements a (discrete) distribution class.
// The distribution object is updated with new values. Its task is to determine
// the length, the average or mean, the variance, the standard deviation, the sum 
// and the squared sum of all values in the distribution. For the scores the length 
// and percentage of every score can be determined.
// The distribution object is not a collection: the values are not stored in the
// object; the state of the object is updated with the new value.
// 

@interface DDiscreteDistribution : Object
{
@private
  long                _length;     // the number of values in the distribution
  double              _sum;        // the sum of the values in the distribution
  double              _sumSquared; // the sum of the squared values in the distribution
  DList              *_scores;     // the list with the scores
}

#endif



@implementation DDiscreteDistribution

  
//// Constructors

//
// Initialise the object with no range
//
// @return the object
//
- (DDiscreteDistribution *) init
{
  [super init];

  _scores = [DList new];
  
  [self reset];
  
  return self;
}


//// Copy related method

//
// Do a shallow copy of the distribution object (not implemented)
// 
// @return the object
// 
- shallowCopy
{
  DDiscreteDistribution *copy = [super shallowCopy];
  
  copy->_scores = [_scores copy];
  
  return copy;
}


//// Destructor

//
// Free the distribution
// 
// @return the object
// 
- free
{
  [_scores free];
  
  return [super free];
}


//// Member methods

//
// Return the number of the values in the distribution
// 
// @return the number
// 
- (int) length
{
  return _length;
}

//
// Return the sum of the values in the distribution
// 
// @return the sum
//
- (double) sum
{
  return _sum;
}

//
// Return the sum of the squared values in the distribution
// 
// @return the sum of the squared values
// 
- (double) sumSquared
{
  return _sumSquared;
}


//// Set methods

//
// Add a score with a range to the distribution: [min,max>
// 
// @param min      the minimum value for the range
// @param max      the maximum value for the range
// 
// @return success
// 
- (BOOL) range :(double) min :(double) max
{
  BOOL ok;
  
  DScore *score = [DScore new];
  
  ok = [score range :min :max];
  
  if (ok)
  {
    [_scores append :score];
  }
  else
  {
    [score free];
  }
  
  return ok;
}

//
// Add a score with a (discrete) range to the distribution: [val-0.5,val+0.5>
// 
// @param value    the discrete value for the range
// 
// @return success
// 
- (BOOL) range :(int) value
{
  BOOL ok;
  
  DScore *score = [DScore new];
  
  ok = [score range :value];
  
  if (ok)
  {
    [_scores append :score];
  }
  else
  {
    [score free];
  }
  
  return ok;
}

//
// Reset the length, sums and scores to zero
// 
// @return the object
// 
- (DDiscreteDistribution *) reset
{
  _length     = 0;
  _sum        = 0.0;
  _sumSquared = 0.0;
  
  [_scores each :@selector(reset)];
  
  return self;
}

//
// Update the distribution with a value
// 
// @param value    the update value
// 
// @return success (is a score updated?)
// 
- (BOOL) update :(double) value
{
  BOOL ok = NO;

  DListIterator *iter = [self scores];

  DScore *score = [iter first];
  
  while ((score != nil) && (!ok))
  {
    if ((value >= [score min]) && (value < [score max]))
    {
      ok = [score update :value];
      
      break;
    }
    
    score = [iter next];
  }
  
  if (ok)
  {
    _length     ++;
    _sum        += value;
    _sumSquared += (value * value);

    score = [iter first];
    
    while (score != nil)
    {
      [score distribution :_length];
      
      score = [iter next];
    }
  }
  
  [iter free];
  
  return ok;
}
      

//// Distribution Calculation methods

//
// Calculate the mean of the values
// 
// @return the mean value
// 
- (double) mean
{
  return [self average];
}

//
// Calculate the average of the values
// 
// @return the average value
// 
- (double) average
{
  if (_length > 0)
  {
    return _sum / (double) _length;
  }
  else
  {
    return 0.0;
  }
}

//
// Calculate the variance of the values
// 
// @return the variance
// 
- (double) variance
{
  if (_length > 0)
  {
    double mean = [self average];
    
    return (_sumSquared - 2.0 * mean * _sum + (double) _length * mean * mean) / (double) _length;
  }
  else
  {
    return 0.0;
  }
}

//
// Calculate the standard deviation of the values
// 
// @return the standard deviation
// 
- (double) standardDeviation
{
  return sqrt([self variance]);
}


//// Score methods

//
// Return an iterator on the scores in the distribution
// 
// @return a (new) iterator on the list of the DScore objects
// 
- (DListIterator *) scores
{
  DListIterator *iter = [DListIterator alloc];
  
  [iter init :_scores];
  
  return iter;
}

@end

/*===========================================================================*/
